Using Cobra for Advanced CLI Applications
Learn how to use Cobra for advanced CLI applications in Go.
We'll cover the following
Cobra is a set of packages that allows a developer to create more complex CLI applications. This becomes more useful than just the standard flag package when the complexity of an application causes a list of flags to become numerous.
In this lesson, we’ll talk about how to use Cobra to create structured CLI applications that are friendly to developers to add features and allow users to understand what is available in an application.
A few features that Cobra provides are as follows:
Nested subcommands
Command suggestions
Aliases for commands so that we can make changes without breaking users
Generation of help text from flags and commands
Generation of auto-completion for various shells
Man page creation
This section will borrow heavily from the Cobra documentation, which you can find here.
Code organization#
To make effective use of Cobra and make it easy for developers to understand where to add and change commands, Cobra suggests the following structure:
This structure has our main main.go executable at the top-level directory and all of our commands under cmd/.
The main file for a Cobra application is primarily used to simply initialize Cobra and let it perform command executions. The file will look like this:
Next, we'll look at using the Cobra generator application to generate boilerplate code.
The optional Cobra generator#
Cobra provides an application that can generate boilerplate code for our application. To get started with the generator, the configuration file for our application in our root directory called ~/.cobra.yaml contains the following:
This will handle printing our MIT license. We can use any of these values for the following built-in licenses:
GPLv2
GPLv3
LGPL
AGPL
2-Clause BSD
3-Clause BSD
By default, Cobra will use this configuration file from our home directory. If we need a different license, put the configuration in our repository and use cobra --config="config/location.yaml to use the alternate configuration file.
To download Cobra and build with the Cobra generator, the following commands are used:
The application can be initialized in the new application's root directory:
Note:
[repo path]will be something such asgithub.com/spf13/newApp.
Commands can be created for the application with the following:
This delivers the following:
Note: We are required to use camelCase for command names. Not doing this will cause us to encounter errors.
The -p option for create is used to make it a subcommand of config. The string that follows is the parent's name plus Cmd. All other add calls have -p set to rootCmd. The application can be run with the following commands after being built:
With the boilerplate now in place, we'll only need to configure the commands to execute.
The command package#
In the cmd package that has been generated, we'll find a file for each command that can be executed. We'll need to modify each file to give the correct help text, use flags, and execute the command. We'll look at a generated cmd/get.go file for an application created with the following commands:
This application will talk to the QOTD server that we created earlier in the course. The generated cmd/get.go file will look similar to this:
This code does the following:
Line 1: Creates a variable whose name is based on the command name plus
Cmd.Line 2:
Useis the argument name for the command line.Line 3:
Shortis the brief description.Line 4:
Longis a longer description with examples.Line 5:
Runis the entry point for the code we want to execute.Lines 10–12: Defines
init(), which adds the command to therootCmdobject.
This is used to write the QOTD CLI:
This code does the following:
Lines 2–10: Sets up an
addrvariable to hold our server address:If
--devis passed, it setsaddrtodevAddr.Otherwise, it uses the
--addrflag's value.--addrdefaults to127.0.0.1:80.
Line 12: Creates a new client for our QOTD server
Line 18: Calls the QOTD server:
Uses
Contextpassed to*cobra.Command.Uses the
--authorflag value, which defaults to an empty string.
Lines 24–39: Uses a
--jsonflag to determine whether the output should be in JSON:If JSON, it outputs an inline-defined struct as JSON.
Otherwise, it just prints it to the screen.
Note: We'll see the
mustBool()andmustString()functions. These simply return the value from the flag name that is passed. If the flag isn't defined, it panics. This removes a lot of ugly code for something that must always work for the CLI application to be valid. These functions are in the repository version.The flags that you see are not from the standard library
flagpackage. Instead, this package uses flag types. This package has more built-in types and methods than the standardflagpackage.
The flags can be defined by using the Run function:
This code does the following:
Line 3: Adds a flag called
--devthat can be shortened to-dand defaults tofalse.Line 4: Adds a flag called
--addrthat defaults to "127.0.0.1:80".Line 5: Adds a flag called
--authorthat can be shortened to-a.Line 6: Adds a flag called
--jsonthat defaults tofalse.
Note: Methods followed by
P, such asBoolP(), define shortened flags as well as the long flag names.
The flags we defined are only available when the get command is invoked. If we create subcommands on get, these will only be available on get with no sub-commands defined.
To add flags that work on all subcommands, use .PersistentFlags() instead of .Flags().
Now, we can run our app and call this command. In the code widget below, the following commands are run:
This runs our application using the server at the 127.0.0.1:3560 address and requests a quote from Eleanor Roosevelt, with output in JSON format:
/
The following command gets a random quote from the server at the address 127.0.0.1:3560
In this lesson, we have learned what the Cobra package is, how to use the Cobra generator tool to bootstrap a CLI application, and finally, how to build commands for our application using this package.
Accessing Non-Flag Arguments
Handling OS Signals